package org.apache.lucene.index;

import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.lucene.util.PriorityQueue;


/**
 * Describe class <code>MultipleTermPositions</code> here.
 *
 * @author Anders Nielsen
 * @version 1.0
 */
public class MultipleTermPositions implements TermPositions {

  private static final class TermPositionsQueue extends PriorityQueue {

    TermPositionsQueue(List termPositions) throws IOException {
      initialize(termPositions.size());

      Iterator i = termPositions.iterator();
      while (i.hasNext()) {
        TermPositions tp = (TermPositions) i.next();
        if (tp.next())
          put(tp);
      }
    }

    final TermPositions peek() {
      return (TermPositions) top();
    }

    public final boolean lessThan(Object a, Object b) {
      return ((TermPositions) a).doc() < ((TermPositions) b).doc();
    }
  }

  private static final class IntQueue {
    private int _arraySize = 16;

    private int _index = 0;
    private int _lastIndex = 0;

    private int[] _array = new int[_arraySize];

    final void add(int i) {
      if (_lastIndex == _arraySize)
        growArray();

      _array[_lastIndex++] = i;
    }

    final int next() {
      return _array[_index++];
    }

    final void sort() {
      Arrays.sort(_array, _index, _lastIndex);
    }

    final void clear() {
      _index = 0;
      _lastIndex = 0;
    }

    final int size() {
      return (_lastIndex - _index);
    }

    private void growArray() {
      int[] newArray = new int[_arraySize * 2];
      System.arraycopy(_array, 0, newArray, 0, _arraySize);
      _array = newArray;
      _arraySize *= 2;
    }
  }

  private int _doc;
  private int _freq;

  private TermPositionsQueue _termPositionsQueue;
  private IntQueue _posList;

  /**
   * Creates a new <code>MultipleTermPositions</code> instance.
   *
   * @param indexReader an <code>IndexReader</code> value
   * @param terms       a <code>Term[]</code> value
   * @throws IOException if an error occurs
   */
  public MultipleTermPositions(IndexReader indexReader, Term[] terms) throws IOException {
    List termPositions = new LinkedList();

    for (int i = 0; i < terms.length; i++)
      termPositions.add(indexReader.termPositions(terms[i]));

    _termPositionsQueue = new TermPositionsQueue(termPositions);
    _posList = new IntQueue();
  }

  /**
   * Describe <code>next</code> method here.
   *
   * @return a <code>boolean</code> value
   * @throws IOException if an error occurs
   * @see TermDocs#next()
   */
  public final boolean next() throws IOException {
    if (_termPositionsQueue.size() == 0)
      return false;

    _posList.clear();
    _doc = _termPositionsQueue.peek().doc();

    TermPositions tp;
    do {
      tp = _termPositionsQueue.peek();

      for (int i = 0; i < tp.freq(); i++)
        _posList.add(tp.nextPosition());

      if (tp.next())
        _termPositionsQueue.adjustTop();
      else {
        _termPositionsQueue.pop();
        tp.close();
      }
    }
    while (_termPositionsQueue.size() > 0 && _termPositionsQueue.peek().doc() == _doc);

    _posList.sort();
    _freq = _posList.size();

    return true;
  }

  /**
   * Describe <code>nextPosition</code> method here.
   *
   * @return an <code>int</code> value
   * @throws IOException if an error occurs
   * @see TermPositions#nextPosition()
   */
  public final int nextPosition()
    throws IOException {
    return _posList.next();
  }

  /**
   * Describe <code>skipTo</code> method here.
   *
   * @param target an <code>int</code> value
   * @return a <code>boolean</code> value
   * @throws IOException if an error occurs
   * @see TermDocs#skipTo(int)
   */
  public final boolean skipTo(int target)
    throws IOException {
    while (target > _termPositionsQueue.peek().doc()) {
      TermPositions tp = (TermPositions) _termPositionsQueue.pop();

      if (tp.skipTo(target))
        _termPositionsQueue.put(tp);
      else
        tp.close();
    }

    return next();
  }

  /**
   * Describe <code>doc</code> method here.
   *
   * @return an <code>int</code> value
   * @see TermDocs#doc()
   */
  public final int doc() {
    return _doc;
  }

  /**
   * Describe <code>freq</code> method here.
   *
   * @return an <code>int</code> value
   * @see TermDocs#freq()
   */
  public final int freq() {
    return _freq;
  }

  /**
   * Describe <code>close</code> method here.
   *
   * @throws IOException if an error occurs
   * @see TermDocs#close()
   */
  public final void close() throws IOException {
    while (_termPositionsQueue.size() > 0)
      ((TermPositions) _termPositionsQueue.pop()).close();
  }

  /**
   * Describe <code>seek</code> method here.
   *
   * @param arg0 a <code>Term</code> value
   * @throws IOException if an error occurs
   * @see TermDocs#seek(Term)
   */
  public void seek(Term arg0) throws IOException {
    throw new UnsupportedOperationException();
  }

  public void seek(TermEnum termEnum) throws IOException {
    throw new UnsupportedOperationException();
  }


  /**
   * Describe <code>read</code> method here.
   *
   * @param arg0 an <code>int[]</code> value
   * @param arg1 an <code>int[]</code> value
   * @return an <code>int</code> value
   * @throws IOException if an error occurs
   * @see TermDocs#read(int[], int[])
   */
  public int read(int[] arg0, int[] arg1) throws IOException {
    throw new UnsupportedOperationException();
  }

}
